use crate::complie::{Token};

pub enum LiteralType {
    STRING(String),
    NUMBER(String),
    BOOLEAN(bool),
    None
}

impl LiteralType {
    fn format(&self) -> String {
        match &self {
            LiteralType::STRING(v)      => v.to_string(),
            LiteralType::NUMBER(v1)     => v1.to_string(),
            LiteralType::BOOLEAN(v3)      => v3.to_string(),
            LiteralType::None                    => "null".to_string(),
        }
    }
}

pub enum Expr {
    // 二元表达式
    // binary         → expression operator expression ;
    Binary { left: Box<Expr>, operator: Token, right: Box<Expr> },
    
    // 分组
    // grouping       → "(" expression ")" ;
    Grouping { expression: Box<Expr> },

    // 字面量
    // literal        → NUMBER | STRING | "true" | "false" | "null" ;
    Literal { value: LiteralType },
    
    // 一元表达式
    // unary          → ( "-" | "!" ) expression ;
    Unary { operator: Token, right: Box<Expr> }
}

impl Accept for Expr {
    fn accept(&self) -> String {
        let r = match &self {
            Expr::Binary { left, operator, right }   => operator.value.to_string()  + &" ".to_string() + &left.accept() + &right.accept(),
            Expr::Grouping { expression }                   => String::from("group") + &" ".to_string() + &expression.accept(),
            Expr::Literal { value }                       => value.format(),
            Expr::Unary { operator, right }         => operator.value.to_string()  + &" ".to_string() + &right.accept(),
            _                                                           => "".to_string(),
        };

        if let Expr::Literal{value: _} = &self {
            r
        } else {
            "(".to_string() + &r + &")".to_string()
        }
    }
}

pub trait Accept {
    fn accept(&self) -> String;
}